Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix autosend scheduling #6121

Merged
merged 24 commits into from
Jun 10, 2024
Merged

Fix autosend scheduling #6121

merged 24 commits into from
Jun 10, 2024

Conversation

seadowg
Copy link
Member

@seadowg seadowg commented May 2, 2024

Closes #6120

This also:

  • Fixes problems introduced recently where form level auto send was no longer working
  • Makes sure that pending auto send jobs will run after an app upgrade (even if there is a change in the underlying code)
  • Will not run "Cellular only" auto send when connected to a metered wifi (like a hotspot), but will retry later (with an exponential back off)

It's probably a good idea to wait on merging this until it's marked "behaviour verified" as we've seen a lot of churn on auto send recently.

Why is this the best possible solution? Were any other approaches considered?

I would have loved to have a more elegant solution for "cellular only", but couldn't find one that would work within the WorkManager world. I think the compromise of having other settings work optimally within Android's current background job setup is correct though.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

This should mean that auto send now behaves more like how we'd "expect". One caveat to highlight is that metered wifi (like a hotspot) will not work with "wifi only" or "cellular only". The work around for this (other than using "wifi or cellular") is to change the wifi to "unmetered" in Android's settings.

Before submitting this PR, please make sure you have:

  • added or modified tests for any new or changed behavior
  • run ./gradlew connectedAndroidTest (or ./gradlew testLab) and confirmed all checks still pass
  • added a comment above any new strings describing it for translators
  • verified that any code or assets from external sources are properly credited in comments and/or in the about file.
  • verified that any new UI elements use theme colors. UI Components Style guidelines

@seadowg
Copy link
Member Author

seadowg commented May 3, 2024

@WKobus could you take another look at this when you get a chance? The biggest change since you last looked is that "cellular only" shouldn't run when connected to a metered wifi (like a hotspot), but should run eventually if you then just connect to cellular (although it might take a few minutes).

@@ -81,7 +81,8 @@ interface Scheduler {

enum class NetworkType {
WIFI,
CELLULAR
CELLULAR,
OTHER
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OTHER would just be any other connected network. Android devices can theoretically have network delivered via Ethernet or Bluetooth for example. These kinds of connections would still satisfy WorkManager's (and our won) "connected" constraint.

* Returns class that can be used to schedule this task using Android's
* WorkManager framework
*/
fun getWorkManagerAdapter(): Class<out WorkerAdapter>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realised we didn't need all this and could just use strings (and no arg constructors) to integrate with WorkManager. It will eventually have to store our class names as strings anyway.

import android.net.ConnectivityManager
import org.odk.collect.async.Scheduler

class ConnectivityProvider(private val context: Context) : NetworkStateProvider {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to replace this implementation at some point, as it's pretty much all deprecated.

@@ -216,6 +214,14 @@ class InstancesDataService(
}
}

fun instanceFinalized(projectId: String, form: Form) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd ideally have a method that's more like:

fun finalizeInstance(projectId: String, instance: Instance)

But the code that finalises a form during form entry and finalises during bulk finalisation is very different at the moment (the former happens pre save to disk), so that's something for another day.

@seadowg seadowg marked this pull request as ready for review May 7, 2024 16:55
@seadowg seadowg requested a review from grzesiek2010 May 7, 2024 16:56
@lognaturel
Copy link
Member

This one would be really great to get in the next beta if possible. @seadowg did you want @getodk/testers to take another look before review or can @grzesiek2010 take a look?

@seadowg
Copy link
Member Author

seadowg commented Jun 6, 2024

@lognaturel Yup! Thanks for the nudge. @WKobus are you able to take another look?

}

HashMap<String, String> inputData = new HashMap<>();
inputData.put(TaskData.DATA_PROJECT_ID, projectId);
scheduler.networkDeferred(getAutoSendTag(projectId), new AutoSendTaskSpec(), inputData, networkType);
scheduler.networkDeferred(getAutoSendTag(projectId), new SendFormsTaskSpec(), inputData, networkConstraint);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need to create objects here like new SendFormsTaskSpec(). those objects are never used and we just need the class name to create an object based on it later. That means you can pass class names directly here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially, I'd need to experiment with TaskSpecWorker using a Kotlin object instead of a class (the way the reflection would work would be different if there's no constructor). Let's look at that when we're next playing with async stuff?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

@grzesiek2010
Copy link
Member

or can @grzesiek2010 take a look?

I was already finishing my review.

@seadowg seadowg merged commit 9833a92 into getodk:master Jun 10, 2024
6 checks passed
@seadowg seadowg deleted the form-level-autosend branch June 10, 2024 16:26
@dbemke
Copy link

dbemke commented Jun 12, 2024

Tested with Success!

Verified on a device with Android 10

Verified Cases:

@WKobus
Copy link

WKobus commented Jun 12, 2024

Tested with Success!

Verified on a device with Android 14

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Forms are not being auto sent after restoring connection that matches auto send setting
5 participants